package com.wwj.core.wawa.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.wwj.core.common.Constants;
import com.wwj.core.server.entity.ServerConfig;
import com.wwj.core.server.entity.mapper.ServerConfigMapper;
import com.wwj.core.system.entity.Parameter;
import com.wwj.core.system.entity.mapper.ParameterMapper;
import com.wwj.core.wawa.entity.*;
import com.wwj.core.wawa.entity.mapper.*;
import com.wwj.core.wawa.service.IPlayerService;
import com.wwj.utils.IDCenterUtil;
import com.wwj.utils.IDGenerator;
import com.wwj.utils.OSSClientUtil;
import com.wwj.utils.StringUtil;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileInputStream;
import java.util.*;

/**
 * Created by Administrator on 2017/11/3.
 */
@Service
public class PlayerServiceImpl implements IPlayerService {

    @Resource
    private PlayerMapper playerMapper;

    @Resource
    private RoomMapper roomMapper;

//    @Resource
//    private ApplyDeliverMapper applyDeliverMapper;
//
//    @Resource
//    private ApplyDeliverDetailMapper applyDeliverDetailMapper;

    @Resource
    private DeliverRecordMapper deliverRecordMapper;

    @Resource
    private DeliverRecordDetailMapper deliverRecordDetailMapper;

    @Resource
    private ItemMapper itemMapper;

    @Resource
    private PlayerItemChangeRecordMapper playerItemChangeRecordMapper;

    @Resource
    private PlayerItemMapper playerItemMapper;

    @Resource
    private PlayRecordMapper playRecordMapper;

    @Resource
    private ServerConfigMapper serverConfigMapper;

    @Resource
    private RoomPlayerMapper roomPlayerMapper;

    @Resource
    private WaWaMachineMapper waWaMachineMapper;

    @Resource
    private ParameterMapper parameterMapper;

    @Resource
    private JedisPool jedisPool;

    @Override
    public void addPlayer(Player player) {
        playerMapper.insertPlayer(player);
    }

    @Override
    public Player findById(Integer id) {
        return playerMapper.selectPlayerById(id);
    }

    @Override
    public Player findByInviteCode(String inviteCode) {
        return playerMapper.selectPlayerByInviteCode(inviteCode);
    }

    @Override
    public void dealBindInvitor(Player player, Player invitor) throws Exception {
        player.setInvitor(invitor.getId());
        playerMapper.updatePlayer(player);

        //邀请送娃娃币
        Parameter parameter = parameterMapper.selectParameterByCode(Constants.PARAM_INVITTE_GIVE);
        if(parameter != null && parameter.getStatus() == 1) {
            int inviteGive = Integer.valueOf(parameter.getParamValue());
            if(inviteGive > 0) {
                parameter = parameterMapper.selectParameterByCode(Constants.PARAM_INVITTE_GIVE_SUM);
                if(parameter != null && parameter.getStatus() == 1) {
                    int inviteGiveSum = Integer.valueOf(parameter.getParamValue());
                    Map<String, Object> params = new HashMap<>();
                    params.put("playerId", player.getId());
                    params.put("subChangeType", PlayerItemChangeRecord.IN_SUB_TYPE_INVITE);
                    int alreadyGet = playerItemChangeRecordMapper.selectSum(params).intValue();
                    if(alreadyGet < inviteGiveSum) {
                        inviteGive =  (inviteGiveSum - alreadyGet) > inviteGive ? inviteGive : (inviteGiveSum - alreadyGet);
                        updatePlayerWaWaCoin(invitor.getId(), PlayerItemChangeRecord.TYPE_INCREASE, PlayerItemChangeRecord.IN_SUB_TYPE_INVITE, inviteGive, 0);
                    }
                } else {
                    updatePlayerWaWaCoin(invitor.getId(), PlayerItemChangeRecord.TYPE_INCREASE, PlayerItemChangeRecord.IN_SUB_TYPE_INVITE, inviteGive, 0);
                }
            }
        }

        //绑定送娃娃币
        parameter = parameterMapper.selectParameterByCode(Constants.PARAM_BIND_GIVE);
        if(parameter != null && parameter.getStatus() == 1) {
            int bindGive = Integer.valueOf(parameter.getParamValue());
            if(bindGive > 0) {
                updatePlayerWaWaCoin(player.getId(), PlayerItemChangeRecord.TYPE_INCREASE, PlayerItemChangeRecord.IN_SUB_TYPE_BIND, bindGive, 0);
            }
        }
    }

    @Override
    public Page<Player> selectPlayerByPage(String search, int limit, int offset) throws Exception {
        PageHelper.offsetPage(offset,limit);
        Map<String,Object> params = new HashMap<String,Object>();
        if(null != search && !"".equals(search)) params.put("search",search);
        return (Page<Player>)playerMapper.selectPlayerByPage(params);
    }

    @Override
    public List<Player> findByRoomId(int roomId) {
        return playerMapper.selectPlayersByRoomId(roomId);
    }

    @Override
    public Page<Player> selectPlayerItemByPage(String search, int offset, int limit) throws Exception {
        PageHelper.offsetPage(offset,limit);
        Map<String,Object> params = new HashMap<String,Object>();
        if(null != search && !"".equals(search)) params.put("search",search);
        return (Page<Player>)playerMapper.selectPlayerIteamByPage(params);
    }

    @Override
    public void dealApplyDeliver(Player player, PlayerAddress playerAddress, List<Integer> playRecordIds) {
        DeliverRecord deliverRecord = new DeliverRecord();
        deliverRecord.setPlayerId(player.getId());
        deliverRecord.setStatus(DeliverRecord.STATUS_APPROVING);
        deliverRecord.setPlayerAddressId(playerAddress.getId());
        deliverRecord.setApplyTime(new Date());
        deliverRecordMapper.insertDeliverRecord(deliverRecord);

        for(Integer playRecordId : playRecordIds) {
            DeliverRecordDetail deliverDetail = new DeliverRecordDetail();
            deliverDetail.setDeliverRecordId(deliverRecord.getId());
            deliverDetail.setPlayRecordId(playRecordId);
            deliverRecordDetailMapper.insertDeliverRecordDetail(deliverDetail);

            PlayRecord playRecord = playRecordMapper.selectPlayRecordById(playRecordId);
            playRecord.setIsDeliver(PlayRecord.UN_DELIVER);
            playRecordMapper.updatePlayRecord(playRecord);
        }
    }

    @Override
    public Player dealLogin(Player player) throws Exception {
        Player p = playerMapper.selectPlayerByWechatOpenId(player.getOpenId());
        if(p == null) {
            player.setNickname(player.getNickname());
            player.setPlayCount(0);
            player.setSuccess(0);
            player.setFailed(0);
            player.setStatus(1);
            player.setRegisterTime(new Date());
            player.setInviteCode(generateInviteCode());
            playerMapper.insertPlayer(player);

            //首次注册送娃娃币
            Parameter parameter = parameterMapper.selectParameterByCode(Constants.PARAM_REGIST_GIVE);
            if(parameter != null && parameter.getStatus() == 1) {
                int registGive = Integer.valueOf(parameter.getParamValue());
                if(registGive > 0) {
                    updatePlayerWaWaCoin(player.getId(), PlayerItemChangeRecord.TYPE_INCREASE, PlayerItemChangeRecord.IN_SUB_TYPE_REGIST, registGive, 0);
                }
            }
            return player;
        } else {
            if(!p.getHeaderIcon().equals(player.getHeaderIcon())) {
                p.setHeaderIcon(player.getHeaderIcon());
                playerMapper.updatePlayer(p);
            }
        }
        return p;
    }

    @Override
    public Map<String, Object> dealPlay(Player player, Room room, WaWaType waWaType) throws Exception {
        //return useDatabaseLocker(player, room, waWaType);
        return useRedisLocker(player, room, waWaType);
    }

    private Map<String, Object> useDatabaseLocker(Player player, Room room, WaWaType waWaType) throws Exception {
        //使用数据库锁来做同步操作
        String lock = "play_room_lock_" + room.getId();
        try {
            Map<String, Object> result = roomMapper.getRoomLocker(lock);
            if (result != null && !result.isEmpty()) {
                if (result.containsKey("roomLock") && ((Long)result.get("roomLock")).intValue() == 1) {
                    room = roomMapper.selectRoomById(room.getId());
                    if(room.getWaWaMachine().getStatus() != WaWaMachine.STATUS_IDLE) {
                        return null;
                    }
                    return play(player, room, waWaType, false);
                }
            }
        } finally {
            roomMapper.releaseRoomLocker(lock);
        }
        return null;
    }

    private Map<String, Object> useRedisLocker(Player player, Room room, WaWaType waWaType) throws Exception {
        //使用Redis来做同步操作
        String lock = "play_room_lock_" + room.getId();
        Jedis jedis = jedisPool.getResource();
        Long re = jedis.setnx(lock, lock);
        if (re == 1) {
            try {
                jedis.expire(lock, 60);//60秒过期

                room = roomMapper.selectRoomById(room.getId());
                if(room.getWaWaMachine().getStatus() != WaWaMachine.STATUS_IDLE) {
                    return null;
                }
                return play(player, room, waWaType, false);
            } finally {
                jedis.del(lock);
                jedis.close();
            }
        }

        return null;
    }

    @Override
    public Room dealEnterRoom(Player player, Room room) {
        //先删除改玩家在其他房间的数据
        List<RoomPlayer> roomPlayers = roomPlayerMapper.selectRoomPlayerByPlayerId(player.getId());
        if(roomPlayers != null && !roomPlayers.isEmpty()) {
            for(RoomPlayer tmp : roomPlayers) {
                Player tp = playerMapper.selectPlayerById(tmp.getPlayerId());
                Room rp = roomMapper.selectRoomById(tmp.getRoomId());
                if(tp != null && rp != null) {
                    dealLeaveRoom(tp, rp);
                }
            }
        }

        RoomPlayer roomPlayer = new RoomPlayer();
        roomPlayer.setRoomId(room.getId());
        roomPlayer.setPlayerId(player.getId());
        roomPlayerMapper.insertRoomPlayer(roomPlayer);
        roomMapper.updateRoomPlayerNumber(room.getId(), 1);
        return roomMapper.selectRoomById(room.getId());
    }

    @Override
    public void dealLeaveRoom(Player player, Room room) {
        dealDequeue(player, room);
        roomMapper.updateRoomPlayerNumber(room.getId(), -1);
        roomPlayerMapper.deleteRoomPlayerByRoomIdAndPlayerId(room.getId(), player.getId());
    }

    @Override
    public Map<String, Object> dealContinuePlay(Player player, Room room, WaWaType waWaType) throws Exception {
        return play(player, room, waWaType, true);
    }

    private Map<String, Object> play(Player player, Room room, WaWaType waWaType, boolean isContinue) throws Exception {
        //1：创建游戏记录
        PlayRecord playRecord = generatePlayRecord(player.getId(), room.getId(), room.getWawaMachineId(), waWaType.getId(), waWaType.getPayItemCount());
        playRecordMapper.insertPlayRecord(playRecord);

        //2：消耗娃娃币
        updatePlayerWaWaCoin(player.getId(), PlayerItemChangeRecord.TYPE_DECREASE, PlayerItemChangeRecord.DE_SUB_TYPE_PLAY, waWaType.getPayItemCount(), playRecord.getId());

        if(!isContinue) {
            //3加入支付队列
            Jedis jedis = jedisPool.getResource();
            JSONObject jsonObj = new JSONObject();
            jsonObj.put("device_id", room.getWaWaMachine().getCode());
            jsonObj.put("user_id", player.getId());
            jsonObj.put("nickname", player.getNickname());
            jsonObj.put("header_icon", player.getHeaderIcon());
            jedis.lpush("payment-queue", jsonObj.toString());
            jedis.close();
        }

        WaWaMachine machine = room.getWaWaMachine();
        if(!isContinue) {
            //4:修改房间状态
            machine.setCurrentPlayerId(player.getId());
            machine.setStatus(WaWaMachine.STATUS_PROGRESS);
        }
        machine.setCurrentKey(playRecord.getToken());
        waWaMachineMapper.update(machine);

        //5：返回game服务器信息
        ServerConfig serverConfig = serverConfigMapper.selectServerConfigByType(ServerConfig.TYPE_GAME);
        Map<String, Object> retMap = new HashMap<>();
        retMap.put("playNo", playRecord.getPlayNo());
        retMap.put("token", playRecord.getToken());
        retMap.put("serverConfig", serverConfig);
        retMap.put("waWaMachine", room.getWaWaMachine());

        //超时处理
        new Thread(new TimeOutAction(playRecord.getToken(), room.getWaWaMachine().getCode())).start();
        return retMap;
    }

    @Override
    public void updateStatus(int id) throws Exception {
        Player player = playerMapper.selectPlayerById(id);
        Map<String, Object> params = new HashMap<String, Object>();
        int status = player.getStatus();
        params.put("id",id);
        if(status == 0){
            params.put("status",1);
        }else{
            params.put("status",0);
        }
        playerMapper.updateStatus(params);
    }

    @Override
    public int updatePlayer(Player player) throws Exception {
        playerMapper.updatePlayer(player);
        return 1;
    }

    @Override
    public void dealPlayTimeOut(String token, String deviceId) throws Exception {
        Map<String, Object> param =  new HashMap<>();
        param.put("deviceId", deviceId);
        List<WaWaMachine> waWaMachines = waWaMachineMapper.selectByParam(param);
        if(waWaMachines != null && !waWaMachines.isEmpty()) {
            WaWaMachine waWaMachine = waWaMachines.get(0);
            if(token.equals(waWaMachine.getCurrentKey())
                    && waWaMachine.getStatus() == WaWaMachine.STATUS_PROGRESS) {
                waWaMachine.setStatus(WaWaMachine.STATUS_IDLE);
                waWaMachine.setCurrentKey(null);
                waWaMachine.setCurrentPlayerId(null);
                waWaMachineMapper.update(waWaMachine);

                PlayRecord playRecord = playRecordMapper.selectPlayRecordByToken(token);
                if(playRecord != null) {
                    //Token未使用，需要退款
                    if(playRecord.getTokenStatus() == PlayRecord.TOKEN_STATUS_NOT_USED) {
                        updatePlayerWaWaCoin(playRecord.getPlayerId(), PlayerItemChangeRecord.TYPE_INCREASE,PlayerItemChangeRecord.IN_SUB_TYPE_REFUND, playRecord.getPayItemNum(),playRecord.getId());
                    }
                    playRecord.setPlayResult(PlayRecord.RESULT_ERROR);
                    playRecord.setTokenStatus(PlayRecord.TOKEN_STATUS_USED);
                    playRecordMapper.updatePlayRecord(playRecord);
                }
            }
        }
    }

    @Override
    public void dealQueue(Player player, Room room) {
        Jedis jedis = jedisPool.getResource();
        Set<String> keys = jedis.keys(Constants.KEY_ROOM_QUEUE + "*");
        for(String key : keys) {
            jedis.zrem(key, player.getId().toString());
        }
        jedis.zadd(Constants.KEY_ROOM_QUEUE + room.getId(), System.currentTimeMillis(), player.getId().toString());
        jedis.close();
    }

    @Override
    public void dealDequeue(Player player, Room room) {
        Jedis jedis = jedisPool.getResource();
        jedis.zrem(Constants.KEY_ROOM_QUEUE + room.getId(), player.getId().toString());
        jedis.close();
    }

    @Override
    public void updatePlayerInfo(Integer id, String nickName, Map<String, Object> headerIcon) throws Exception {
        Player player = playerMapper.selectPlayerById(id);
        player.setNickname(nickName);
        if(headerIcon != null) {
            String filePath = uploadToOOS(id, headerIcon);
            player.setHeaderIcon(OSSClientUtil.URL_PREFIX + filePath);
        }
        playerMapper.updatePlayer(player);
    }

    @Override
    public void updatePlayerPlaySuccess(Integer id) {
        playerMapper.updatePlayerPlaySuccess(id);
    }

    @Override
    public void updatePlayerPlayFailed(Integer id) {
        playerMapper.updatePlayerPlayFailed(id);
    }

    @Override
    public Page<Player> findRankByPage(int offset, int limit) {
        PageHelper.offsetPage(offset, limit);
        return (Page<Player>)playerMapper.selectPlayerRank();
    }

    @Override
    public Page<Player> findRoomRankByPage(int roomId, int offset, int limit) {
        PageHelper.offsetPage(offset, limit);
        return (Page<Player>)playerMapper.selectRoomPlayerRank(roomId);
    }

    @Override
    public List<Player> selectPlayerByInvitorInPage(Integer invitorId, String search, int limit, int offset) {
        PageHelper.offsetPage(offset, limit);
        return (Page<Player>)playerMapper.selectPlayerByInvitor(invitorId, search);
    }

    private String uploadToOOS(int playerId, Map<String, Object> fileInfo) throws Exception {
        File originalFile = (File) fileInfo.get("file");
        String folderPath = "wwj/files/" + playerId + "/header/";
        String fileName = (String)fileInfo.get("name");
        int index = fileName.lastIndexOf(".");
        if(index >= 0) {
            fileName = fileName.substring(index);
        }
        fileName = System.currentTimeMillis() + fileName;
        OSSClientUtil.putFile(folderPath, fileName, new FileInputStream(originalFile));
        return folderPath + fileName;
    }

    private void updatePlayerWaWaCoin(int playerId, int changeType, int subChangeType, int changeNum, int playRecordId) throws Exception {
        Item baseItem = itemMapper.selectItemsByType(1).get(0);
        PlayerItem playerItem = playerItemMapper.selectPlayerItemByPlayerId(playerId);
        int oldNum =  playerItem == null ? 0 : playerItem.getItemNum();
        int newNum = oldNum;
        if(changeType == PlayerItemChangeRecord.TYPE_INCREASE) {
            newNum += changeNum;
        } else if(changeType == PlayerItemChangeRecord.TYPE_DECREASE) {
            newNum -= changeNum;
        }
        PlayerItemChangeRecord playerItemChangeRecord = new PlayerItemChangeRecord();
        playerItemChangeRecord.setPlayerId(playerId);
        playerItemChangeRecord.setItemId(baseItem.getId());
        playerItemChangeRecord.setItemNum(changeNum);
        playerItemChangeRecord.setChangeType(changeType);
        playerItemChangeRecord.setSubChangeType(subChangeType);
        playerItemChangeRecord.setOldNum(oldNum);
        playerItemChangeRecord.setNewNum(newNum);
        playerItemChangeRecord.setPlayRecordId(playRecordId);
        if(changeType == PlayerItemChangeRecord.TYPE_INCREASE) {
            playerItemChangeRecord.setDescription("添加道具，类型=" + baseItem.getId()+ ",道具名称=" + baseItem.getName() + ",数量=" + changeNum);
        } else if(changeType == PlayerItemChangeRecord.TYPE_DECREASE) {
            playerItemChangeRecord.setDescription("消耗道具，类型=" + baseItem.getId()+ ",道具名称=" + baseItem.getName() + ",数量=" + changeNum);
        }
        playerItemChangeRecord.setChangeTime(new Date());
        playerItemChangeRecordMapper.insertPlayerItemChangeRecord(playerItemChangeRecord);

        if(playerItem == null) {
            playerItem = new PlayerItem();
            playerItem.setPlayerId(playerId);
            playerItem.setItemId(baseItem.getId());
            playerItem.setItemNum(newNum);
            playerItemMapper.insertPlayerItem(playerItem);
        } else {
            if(changeType == PlayerItemChangeRecord.TYPE_INCREASE) {
                playerItem.setItemNum(changeNum);
            } else if(changeType == PlayerItemChangeRecord.TYPE_DECREASE) {
                playerItem.setItemNum(-changeNum);
            }
            playerItemMapper.updatePlayerItem(playerItem);
        }
    }

    private PlayRecord generatePlayRecord(int playerId, int roomId,int waWaMachineId, int waWaTypeId, int payItemNum) {
        PlayRecord playRecord = new PlayRecord();
        playRecord.setWawaMachineId(waWaMachineId);
        playRecord.setPayItemNum(payItemNum);
        playRecord.setPlayerId(playerId);
        playRecord.setPlayResult(PlayRecord.RESULT_INIT);
        playRecord.setPlayTime(new Date());
        playRecord.setVideoStatus(PlayRecord.NO_VIDEO);
        playRecord.setVideoUrl(null);
        playRecord.setIsDeliver(PlayRecord.UN_APPLY);
        playRecord.setWawaTypeId(waWaTypeId);
        playRecord.setRoomId(roomId);
        playRecord.setPlayNo(IDCenterUtil.getInstance().generateOrderId());
        playRecord.setToken(IDGenerator.getUUID());
        return playRecord;
    }

    private String generateInviteCode() {
        String inviteCode = StringUtil.getRandomStringByLength(7);
        Player player = playerMapper.selectPlayerByInviteCode(inviteCode);
        while(player != null) {
            inviteCode = StringUtil.getRandomStringByLength(7);
            player = playerMapper.selectPlayerByInviteCode(inviteCode);
        }
        return inviteCode;
    }

    final class TimeOutAction implements Runnable {

        private String token;

        private String deviceId;

        public TimeOutAction(String token, String deviceId) {
            this.token = token;
            this.deviceId = deviceId;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(45 * 1000);
                dealPlayTimeOut(token,  deviceId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
